package com.example.authority.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.authority.common.entity.MyConst;
import com.example.authority.common.entity.SimpleGrantedAuthority;
import com.example.authority.common.utils.RedisUtil;
import com.example.authority.domain.*;
import com.example.authority.mapper.*;
import com.example.authority.service.UserService;
import com.example.other.domain.UsersInfo;
import com.example.other.mapper.UsersInfoMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

/**
 * @author Limhuang
 * @description 针对表【sys_user】的数据库操作Service实现
 * @createDate 2024-06-23 22:12:37
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
        implements UserService {

    @Autowired
    private UsersInfoMapper usersInfoMapper;
    @Autowired
    private UserMapper userMapper;

    @Autowired
    private MenuMapper menuMapper;

    @Autowired
    private RoleMenuMapper roleMenuMapper;

    @Autowired
    private RoleMapper roleMapper;

    @Autowired
    private UserRoleMapper userRoleMapper;

    @Autowired
    private BCryptPasswordEncoder bCryptPasswordEncoder;

    @Autowired
    private RedisUtil redisUtil;
    @Value("${jwt.expiration}")
    private Integer expireTime;

    @Override
    public User getUserByUserName(String username) {
        List<User> users = userMapper.selectList(new QueryWrapper<User>().lambda().eq(User::getUsername, username));
        if (users.size() > 0) {
            User user = users.get(0);
            user.setRoles(getRoles(username));
            user.setMenus(getMenus(username));
            user.setAuthorities(getAuthorities(user.getId()));
            return users.get(0);
        }
        return null;
    }

    @Override
    public User register(User user) {
        List<User> users = userMapper.selectList(new QueryWrapper<User>().lambda().eq(User::getUsername, user.getUsername()));
        if (users != null && users.size() > 0) {
            throw new RuntimeException("用户已存在");
        }
        user.setCreated(new Date());
        user.setUpdated(new Date());
        String password = user.getPassword();
        password = bCryptPasswordEncoder.encode(password);
        user.setPassword(password);
        userMapper.insert(user);
        addRole(user.getId(), 3L);
        return user;
    }

    @Override
    @Transactional
    public User registerByPhone(RegisterForm registerForm) {
        if(registerForm == null){
            throw new RuntimeException("注册信息为空");
        }
        if(registerForm.getRePassword() == null || !registerForm.getRePassword().contentEquals(registerForm.getPassword())){
            throw new RuntimeException("两次密码不一致");
        }
        User user = new User();
        user.setUsername(registerForm.getUsername());
        user.setPhone(registerForm.getPhone());
        user.setPassword(registerForm.getPassword());


        UsersInfo usersInfo = new UsersInfo();
        usersInfo.setRealname(registerForm.getRealName());
        usersInfo.setSex(registerForm.getSex());
        usersInfo.setBirthday(registerForm.getBirthday());
        usersInfo.setIdentitycard(registerForm.getIdCard());

        String code = registerForm.getCode();
        // Redis验证短信验证码的正确性
        if (!(code.equals(redisUtil.hget(MyConst.CAPTCHA_KEY, MyConst.SMS_KEY + user.getPhone())))) {
            throw new RuntimeException("短信验证码错误");
        }
        List<User> users = userMapper.selectList(new QueryWrapper<User>().lambda().eq(User::getUsername, user.getUsername()));
        if (users != null && users.size() > 0) {
            throw new RuntimeException("该账号已注册");
        }
        user.setCreated(new Date());
        user.setUpdated(new Date());
        register(user);
        addRole(user.getId(), 3L);

        usersInfo.setSysUserId(user.getId());
        usersInfoMapper.insert(usersInfo);

        return user;
    }


    @Override
    public Set<GrantedAuthority> getAuthorities(Long userId) {
        String username = userMapper.selectById(userId).getUsername();
        String key = username + MyConst.AUTHENTICATION;
        if (redisUtil.hasKey(key)) {
            return (Set<GrantedAuthority>) redisUtil.get(key);
        }
        QueryWrapper<UserRole> queryWrapper = new QueryWrapper();
        queryWrapper.lambda().eq(UserRole::getUserId, userId);
        List<UserRole> userRoles = userRoleMapper.selectList(queryWrapper);
        List<Long> roleIds = new ArrayList<>();
        userRoles.stream().forEach(userRole -> {
            roleIds.add(userRole.getRoleId());
        });

        if (roleIds.size() == 0) {
            return new HashSet<>();
        }
        List<Role> roles = roleMapper.selectBatchIds(roleIds);

        List<RoleMenu> roleMenus = new ArrayList<>();
        QueryWrapper<RoleMenu> roleMenuQueryWrapper = new QueryWrapper<>();
        roleMenuQueryWrapper.lambda().in(RoleMenu::getRoleId, roleIds);
        roleMenus = roleMenuMapper.selectList(roleMenuQueryWrapper);

        List<Long> menuIds = new ArrayList<>();
        roleMenus.stream().forEach(roleMenu -> {
            menuIds.add(roleMenu.getMenuId());
        });
        List<Menu> menus = menuMapper.selectBatchIds(menuIds);

        if (menus.size() == 0 && roles.size() == 0) {
            return new HashSet<>();
        }
        Set<GrantedAuthority> grantedAuthorities = new HashSet<>();
        for (int i = 0; i < menus.size(); i++) {
            SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority(menus.get(i).getPerms());
            grantedAuthorities.add(simpleGrantedAuthority);
        }
        for (int i = 0; i < roles.size(); i++) {
            SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority("ROLE_" + roles.get(i).getCode());
            grantedAuthorities.add(simpleGrantedAuthority);
        }


        redisUtil.set(key, grantedAuthorities, 60 * expireTime);
        return (Set<GrantedAuthority>) redisUtil.get(key);
    }

    @Override
    public Set<Role> getRoles(String username) {
        String key = username + MyConst.ROLE;
        if (redisUtil.hasKey(key)) {
            return (Set<Role>) redisUtil.get(key);
        }
        Set<Role> roles = new HashSet<>();
        List<User> users = userMapper.selectList(new QueryWrapper<User>().lambda().eq(User::getUsername, username));
        if (users.size() > 0) {
            User user = users.get(0);
            QueryWrapper<UserRole> queryWrapper = new QueryWrapper();
            queryWrapper.lambda().eq(UserRole::getUserId, user.getId());
            List<UserRole> userRoles = userRoleMapper.selectList(queryWrapper);
            List<Long> roleIds = new ArrayList<>();
            userRoles.stream().forEach(userRole -> {
                roleIds.add(userRole.getRoleId());
            });
            if (roleIds.size() > 0) {
                roles = new HashSet<>(roleMapper.selectBatchIds(roleIds));
            }
        }
        redisUtil.set(key, roles, 60 * expireTime);
        return (Set<Role>) redisUtil.get(key);
    }

    @Override
    public List<Menu> getMenus(String username) {
        String key = username + MyConst.MENU;
        if (redisUtil.hasKey(key)) {
            return (List<Menu>) redisUtil.get(key);
        }
        List<Menu> menus = new ArrayList<>();
        Set<Role> roles = getRoles(username);
        List<Long> roleIds = new ArrayList<>();
        roles.stream().forEach(role -> {
            roleIds.add(role.getId());
        });
        if (roleIds.size() > 0) {
            List<RoleMenu> roleMenus;
            QueryWrapper<RoleMenu> roleMenuQueryWrapper = new QueryWrapper<>();
            roleMenuQueryWrapper.lambda().in(RoleMenu::getRoleId, roleIds);
            roleMenus = roleMenuMapper.selectList(roleMenuQueryWrapper);

            List<Long> menuIds = new ArrayList<>();
            roleMenus.stream().forEach(roleMenu -> {
                menuIds.add(roleMenu.getMenuId());
            });
            if (menuIds.size() > 0) {
                menus = menuMapper.selectBatchIds(menuIds);
            }
        }
        redisUtil.set(key, menus, 60 * expireTime);
        return (List<Menu>) redisUtil.get(key);
    }

    @Override
    public User addRole(Long userId, Long roleId) {
        UserRole userRole = new UserRole(null, userId, roleId);
        userRoleMapper.insert(userRole);
        return null;
    }

    @Override
    public User addRoles(Long userId, List<Long> roleIds) {
        for (Long roleId :
                roleIds) {
            userRoleMapper.insert(new UserRole(null, userId, roleId));
        }
        return null;
    }

    @Override
    public User removeRole(Long userId, Long roleId) {
        userRoleMapper.delete(new QueryWrapper<UserRole>().lambda().eq(UserRole::getUserId, userId).eq(UserRole::getRoleId, roleId));
        return null;
    }

    @Override
    public User removeRoles(Long userId, List<Long> roleIds) {
        for (Long roleId :
                roleIds) {
            userRoleMapper.delete(new QueryWrapper<UserRole>().lambda().eq(UserRole::getUserId, userId).eq(UserRole::getRoleId, roleId));
        }
        return null;
    }

    @Override
    public User updateRoles(Long userId, List<Long> roleIds) {
        userRoleMapper.delete(new QueryWrapper<UserRole>().lambda().eq(UserRole::getUserId, userId));
        addRoles(userId, roleIds);
        return null;
    }


}




